package cn.tangcent.rules;

import cn.tangcent.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by TangMing on 2017/4/8.
 */
public class GeminiRule implements SudokuRule {

    private Set<Integer> cache = new HashSet<>();

    @Override
    public boolean check(SudokuData sudokuData) {
        Holder<Boolean> resultHolder = new Holder<>(false);
        Coordinate[] rests = sudokuData.rests().stream().map(SudokuUtils::index2XY).toArray(Coordinate[]::new);
        Integer[] optionals = sudokuData.optionals(rests);
        for (int i = 0; i < optionals.length; i++) {
            if (SudokuUtils.oneBits(optionals[i]) == 2) {//有两个可选项
                int optional = optionals[i];
                Coordinate coordinate = rests[i];
                SudokuUtils.sibling(rests[i], sibling -> {
                    //两个位置同时在两个相同的数字上选择
                    if (sudokuData.optional(sibling.getX(), sibling.getY()) == optional) {
                        if (SudokuUtils.cache(cache, build(coordinate, sibling))) {
                            resultHolder.setTarget(true);
                            ensure(sudokuData, coordinate, sibling, optional);
                        }
                    }
                });
            }
        }
        return resultHolder.getTarget();
    }

    /**
     * @param one     -第一个位置
     * @param another -第二个位置
     * @param options -擦除的选项
     */
    private void ensure(SudokuData sudokuData, Coordinate one, Coordinate another, int options) {
        Set<Integer> cache = new HashSet<>();
        SudokuUtils.cache(cache, SudokuUtils.toIndex(one));
        SudokuUtils.cache(cache, SudokuUtils.toIndex(another));
        List<Coordinate> ensureEleList = new ArrayList<>();
        if (SudokuUtils.sameRow(one, another)) {
            Collections.addAll(ensureEleList, SudokuUtils.entireRow(one.getX()));
        }
        if (SudokuUtils.sameColumn(one, another)) {
            Collections.addAll(ensureEleList, SudokuUtils.entireColumn(one.getY()));
        }
        if (SudokuUtils.sameBlock(one, another)) {
            Collections.addAll(ensureEleList, SudokuUtils.entireBlock(SudokuUtils.toZ(one)));
        }
        ensureEleList = ensureEleList.stream().filter(coordinate -> SudokuUtils.cache(cache, SudokuUtils.toIndex(coordinate))).collect(Collectors.toList());
        Coordinate[] ensureEles = sudokuData.todos(ensureEleList.toArray(new Coordinate[ensureEleList.size()]));
        sudokuData.ensure(ensureEles, options);
    }

    private int build(Coordinate one, Coordinate another) {
        int oneIndex = SudokuUtils.toIndex(one);
        int anotherIndex = SudokuUtils.toIndex(another);
        return (Integer.max(oneIndex, anotherIndex) << 8) + Integer.min(oneIndex, anotherIndex);
    }
}
